﻿/*======================================================================
== Copyright : BlueCurve (c)
== Licence   : Gnu/GPL v2.x
== Author    : Teddy Albina
== Email     : bluecurveteam@gmail.com
== Web site  : http://www.codeplex.com/BlueCurve
========================================================================*/
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;


namespace BlueCurve.Common.Ngrams
{
    /// <summary>
    /// Ngrams class
    /// </summary>
    /// <example>
    /// <![CDATA[
    ///     SortedList<float, string> res = new SortedList<float, string>();
    ///     Dictionary<string, string> d = new Dictionary<string, string>();
    ///
    ///     d.Add("fr", "bonjour mon nom est bluecurve comment allez vous ?");
    ///     d.Add("en", "you want em' i got em' drippin' like water");
    ///     d.Add("rn", "ff 65842 qzrar 565456");
    ///     d.Add("SD", "ffr dsd sdsDsdSDQSsqds");
    ///
    ///     foreach (KeyValuePair<string, string> t in d)
    ///     {
    ///         float temp = NGram.GetTrigramSimilarity(t.Value, "bonjour je suis teddy");
    ///         if (!res.ContainsKey(temp))
    ///             res.Add(temp, t.Key);
    ///     }
    ///     Console.WriteLine(res.Values[0] + " => " + res.Keys[0]);
    ///     Console.WriteLine(res.Values[res.Count-1] + " => " + res.Keys[res.Count-1]);
    /// ]]>
    /// </example>
    public static class NGram
    {
        /// <summary>
        /// Generate a liste of ngrams
        /// </summary>
        /// <param name="text">Text to tokeniz</param>
        /// <param name="gramLength">Ngram length</param>
        /// <returns><![CDATA[List<string>]]></returns>
        public static List<string> GenerateNGrams(string text, int gramLength)
        {
            if (text == null || text.Length == 0)
                return null;

            List<string> grams = new List<string>();
            int length = text.Length;
            if (length < gramLength)
            {
                string gram;
                for (int i = 1; i <= length; i++)
                {
                    gram = text.Substring(0, (i) - (0));
                    if (grams.IndexOf(gram) == -1)
                        grams.Add(gram);
                }

                gram = text.Substring(length - 1, (length) - (length - 1));
                if (grams.IndexOf(gram) == -1)
                    grams.Add(gram);

            }
            else
            {
                for (int i = 1; i <= gramLength - 1; i++)
                {
                    string gram = text.Substring(0, (i) - (0));
                    if (grams.IndexOf(gram) == -1)
                        grams.Add(gram);
                }

                for (int i = 0; i < (length - gramLength) + 1; i++)
                {
                    string gram = text.Substring(i, (i + gramLength) - (i));
                    if (grams.IndexOf(gram) == -1)
                        grams.Add(gram);
                }

                for (int i = (length - gramLength) + 1; i < length; i++)
                {
                    string gram = text.Substring(i, (length) - (i));
                    if (grams.IndexOf(gram) == -1)
                        grams.Add(gram);
                }
            }

            return (from y in grams.AsQueryable()
                    where y.Length == gramLength
                    select y).ToList();
        }


        /// <summary>
        /// Compute ngram similarity
        /// </summary>
        /// <param name="text1">Texte de base</param>
        /// <param name="text2">Text à comparer</param>
        /// <param name="gramlength">Longueur des grams</param>
        /// <returns>float</returns>
        private static float ComputeNGramSimilarity(string text1, string text2, int gramlength)
        {
            if ((object)text1 == null || (object)text2 == null || text1.Length == 0 || text2.Length == 0)
                return 0.0F;
            string[] grams1 = GenerateNGrams(text1, gramlength).ToArray();
            string[] grams2 = GenerateNGrams(text2, gramlength).ToArray();
            int count = 0;
            for (int i = 0; i < grams1.Length; i++)
            {
                for (int j = 0; j < grams2.Length; j++)
                {
                    if (!grams1[i].Equals(grams2[j]))
                        continue;
                    count++;
                    break;
                }
            }
            return (2.0F * (float)count) / (float)(grams1.Length + grams2.Length);
        }


        /// <summary>
        /// Compute ngram similarity
        /// </summary>
        /// <param name="text1">Liste de textes de base</param>
        /// <param name="text2">Texte à tester</param>
        /// <param name="gramlength">Longueur des grams</param>
        /// <returns>float</returns>
        private static float ComputeNGramSimilarity(List<string> text1, string text2, int gramlength)
        {
            if (text1 == null || (object)text2 == null || text1.Count == 0 || text2.Length == 0)
                return 0.0F;
            string[] grams1 = text1.ToArray();
            string[] grams2 = GenerateNGrams(text2, gramlength).ToArray();
            int count = 0;
            for (int i = 0; i < grams1.Length; i++)
            {
                for (int j = 0; j < grams2.Length; j++)
                {
                    if (!grams1[i].Equals(grams2[j]))
                        continue;
                    count++;
                    break;
                }
            }
            return (2.0F * (float)count) / (float)(grams1.Length + grams2.Length);
        }


        /// <summary>
        /// Compute the similarity of Bigram
        /// </summary>
        public static float GetBigramSimilarity(string text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 2);
        }

        public static float GetBigramSimilarity(List<string> text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 2);
        }


        /// <summary>
        /// Compute the similarity of Trigram
        /// </summary>
        public static float GetTrigramSimilarity(string text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 3);
        }

        public static float GetTrigramSimilarity(List<string> text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 3);
        }


        /// <summary>
        /// Compute the similarity of Quadgram
        /// </summary>
        public static float GetQuadGramSimilarity(string text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 4);
        }

        public static float GetQuadGramSimilarity(List<string> text1, string text2)
        {
            return ComputeNGramSimilarity(text1, text2, 4);
        }
    }
}
